import json

import os
from typing import Dict, Any, Optional, Union
from pathlib import Path

# 导入项目中的 JsonObject 类，用于创建映射对象
from ..utils.json_object import JsonObject
from ..utils.logger import get_logger

# 添加日志记录器
logger = get_logger("driver.config")

class DeviceAddressMapping:
    """设备地址映射配置管理器（单例模式）"""

    _instance = None
    _initialized = False

    def __new__(cls, config_dir: Optional[str] = None):
        """
        实现单例模式
        """
        if cls._instance is None:
            cls._instance = super(DeviceAddressMapping, cls).__new__(cls)
        return cls._instance

    def __init__(self, config_dir: Optional[str] = None):
        """
        初始化设备地址映射配置管理器

        Args:
            config_dir: 配置目录路径（可选）
        """
        # 防止重复初始化
        if DeviceAddressMapping._initialized:
            return

        try:
            # 使用基于模块文件位置的方式处理默认配置目录
            current_dir = os.path.dirname(os.path.abspath(__file__))
            driver_root = os.path.dirname(current_dir)
            self.config_dir = config_dir or os.path.join(driver_root, "config", "device_interface")
            self.mapping_data: Dict[str, Any] = {}
            self.device_config_mapping: Dict[str, str] = {}  # 存储设备ID到配置文件路径的映射

            # 加载所有地址映射配置
            self.load_all_mappings()

            DeviceAddressMapping._initialized = True
        except Exception as e:
            logger.error(f"DeviceAddressMapping initialization failed: {e}")
            raise

    def register_device_config(self, device_id: str, config_path: str) -> bool:
        """
        注册设备与其地址映射配置文件的关联

        Args:
            device_id: 设备ID
            config_path: 配置文件路径

        Returns:
            bool: 是否注册成功
        """
        try:
            self.device_config_mapping[device_id] = config_path
            logger.info(f"Registered device {device_id} with config path {config_path}")
            return True
        except Exception as e:
            logger.error(f"Error registering device {device_id} with config path {config_path}: {e}")
            return False

    def load_all_mappings(self) -> bool:
        """
        根据已注册的设备配置加载所有地址映射配置文件

        Returns:
            bool: 是否加载成功
        """
        try:
            success_count = 0
            for device_id, config_path in self.device_config_mapping.items():
                if self.load_mapping_by_device(device_id, config_path):
                    success_count += 1
                    logger.info(f"Loaded address mapping for device {device_id} from {config_path}")
                else:
                    logger.warning(f"Failed to load address mapping for device {device_id} from {config_path}")

            logger.info(f"Successfully loaded {success_count} of {len(self.device_config_mapping)} device mappings")
            return True
        except Exception as e:
            logger.error(f"Error loading address mappings: {e}")
            return False

    def load_mapping_by_device(self, device_id: str, config_path: str) -> bool:
        """
        根据设备ID和配置文件路径加载地址映射配置

        Args:
            device_id: 设备ID
            config_path: 配置文件路径（相对或绝对路径）

        Returns:
            bool: 是否加载成功
        """
        try:
            # 处理相对路径和绝对路径
            if os.path.isabs(config_path):
                full_config_path = config_path
            else:
                driver_root = ""
                # 构建相对于 driver 目录的完整路径
                full_config_path = os.path.join(driver_root, config_path)

            # 检查文件是否存在
            if os.path.exists(full_config_path):
                with open(full_config_path, 'r', encoding='utf-8') as f:
                    mapping_data = json.load(f)
                self.mapping_data[device_id] = mapping_data
                logger.info(f"Address mapping loaded for device {device_id} from {full_config_path}")
                return True
            else:
                logger.warning(f"Address mapping file {full_config_path} not found for device {device_id}")
                return False
        except json.JSONDecodeError as e:
            logger.error(f"Address mapping file {full_config_path} format error for device {device_id}: {e}")
            return False
        except Exception as e:
            logger.error(f"Error loading address mapping for device {device_id} from {full_config_path}: {e}")
            return False

    def load_mapping_by_path(self, device_id: str, config_path: str, project_root: str) -> bool:
        """
        根据配置路径加载地址映射配置（支持多种路径解析方式）

        Args:
            device_id: 设备ID
            config_path: 配置文件路径
            project_root: 项目根目录路径

        Returns:
            bool: 是否加载成功
        """
        # 如果是绝对路径，直接使用
        if os.path.isabs(config_path):
            if os.path.exists(config_path):
                # 注册设备配置映射
                self.register_device_config(device_id, config_path)
                # 加载映射配置
                return self.load_mapping_by_device(device_id, config_path)
        else:
            # 如果是相对路径，则相对于项目根目录
            full_path = os.path.join(project_root, config_path)
            if os.path.exists(full_path):
                # 注册设备配置映射
                self.register_device_config(device_id, full_path)
                # 加载映射配置
                return self.load_mapping_by_device(device_id, full_path)
            else:
                # 尝试在driver目录下查找
                driver_path = os.path.join(project_root, "driver", config_path)
                if os.path.exists(driver_path):
                    # 注册设备配置映射
                    self.register_device_config(device_id, config_path)
                    # 加载映射配置
                    return self.load_mapping_by_device(device_id, config_path)
        return False

    def load_default_mapping(self, device_id: str, project_root: str, class_name: str) -> bool:
        """
        加载默认地址映射配置文件

        Args:
            device_id: 设备ID
            project_root: 项目根目录路径
            class_name: 设备类名

        Returns:
            bool: 是否加载成功
        """
        # 在driver/device/config目录下查找与设备模块名类名一致的.json配置文件
        default_config_path = os.path.join(
            project_root,
            "driver",
            "device",
            "config",
            f"{class_name.lower()}communication.json"
        )
        if os.path.exists(default_config_path):
            # 注册设备配置映射
            self.register_device_config(device_id, default_config_path)
            # 加载映射配置
            return self.load_mapping_by_device(device_id, default_config_path)
        return False

    def load_mapping(self, config_file: str, device_id: str) -> bool:
        """
        加载单个地址映射配置文件（保持向后兼容性）

        Args:
            config_file: 配置文件路径
            device_id: 设备ID

        Returns:
            bool: 是否加载成功
        """
        try:
            # 处理相对路径和绝对路径
            if os.path.isabs(config_file):
                full_config_path = config_file
            else:
                # 获取当前文件所在目录作为基准目录
                current_dir = os.path.dirname(os.path.abspath(__file__))
                # 回退两级目录到达 driver 目录 (driver/config -> driver)
                driver_root = os.path.dirname(current_dir)
                # 构建相对于 driver 目录的完整路径
                full_config_path = os.path.join(driver_root, config_file)

            if os.path.exists(full_config_path):
                with open(full_config_path, 'r', encoding='utf-8') as f:
                    mapping_data = json.load(f)
                self.mapping_data[device_id] = mapping_data
                logger.info(f"Address mapping loaded from {full_config_path} for device {device_id}")
                return True
            else:
                logger.warning(f"Address mapping file {full_config_path} not found")
                return False
        except json.JSONDecodeError as e:
            logger.error(f"Address mapping file {full_config_path} format error: {e}")
            return False
        except Exception as e:
            logger.error(f"Error loading address mapping from {full_config_path}: {e}")
            return False

    def get_mapping(self, device_id: str, return_type: str = "dict") -> Optional[Union[Dict[str, Any], JsonObject]]:
        """
        获取指定设备的地址映射配置

        Args:
            device_id: 设备ID
            return_type: 返回类型，"dict" 返回字典，"object" 返回 JsonObject 对象

        Returns:
            Dict[str, Any] 或 JsonObject: 地址映射配置，如果未找到则返回None
        """
        mapping_data = self.mapping_data.get(device_id)
        if mapping_data is None:
            return None

        if return_type == "object":
            return JsonObject(mapping_data)
        else:
            return mapping_data



    def set_mapping(self, device_id: str, mapping_data: Dict[str, Any]) -> bool:
        """
        设置指定设备的地址映射配置

        Args:
            device_id: 设备ID
            mapping_data: 地址映射配置数据

        Returns:
            bool: 是否设置成功
        """
        try:
            self.mapping_data[device_id] = mapping_data
            logger.info(f"Address mapping set for device {device_id}")
            return True
        except Exception as e:
            logger.error(f"Error setting address mapping for device {device_id}: {e}")
            return False

    def save_mapping(self, device_id: str, config_file: Optional[str] = None) -> bool:
        """
        保存指定设备的地址映射配置到文件

        Args:
            device_id: 设备ID
            config_file: 配置文件路径（可选）

        Returns:
            bool: 是否保存成功
        """
        try:
            if device_id not in self.mapping_data:
                logger.warning(f"No address mapping data for device {device_id}")
                return False

            # 如果没有指定文件路径，则使用默认路径
            if not config_file:
                # 使用基于模块文件位置的方式处理默认配置目录
                current_dir = os.path.dirname(os.path.abspath(__file__))
                driver_root = os.path.dirname(current_dir)
                config_dir = os.path.join(driver_root, self.config_dir)
                config_file = os.path.join(config_dir, f"{device_id}_communication.json")

            # 确保目录存在
            os.makedirs(os.path.dirname(config_file), exist_ok=True)

            with open(config_file, 'w', encoding='utf-8') as f:
                json.dump(self.mapping_data[device_id], f, ensure_ascii=False, indent=2)
            logger.info(f"Address mapping saved to {config_file} for device {device_id}")
            return True
        except Exception as e:
            logger.error(f"Error saving address mapping for device {device_id} to {config_file}: {e}")
            return False

    def remove_mapping(self, device_id: str) -> bool:
        """
        移除指定设备的地址映射配置

        Args:
            device_id: 设备ID

        Returns:
            bool: 是否移除成功
        """
        try:
            if device_id in self.mapping_data:
                del self.mapping_data[device_id]
                logger.info(f"Address mapping removed for device {device_id}")
                return True
            else:
                logger.warning(f"Address mapping not found for device {device_id}")
                return False
        except Exception as e:
            logger.error(f"Error removing address mapping for device {device_id}: {e}")
            return False

    def get_all_mappings(self, return_type: str = "dict") -> Union[Dict[str, Any], Dict[str, JsonObject]]:
        """
        获取所有地址映射配置

        Args:
            return_type: 返回类型，"dict" 返回字典，"object" 返回包含 JsonObject 对象的字典

        Returns:
            Dict[str, Any] 或 Dict[str, JsonObject]: 所有地址映射配置数据
        """
        if return_type == "object":
            return {device_id: JsonObject(mapping_data) for device_id, mapping_data in self.mapping_data.items()}
        else:
            return self.mapping_data.copy()


def get_device_address_manager(config_dir: Optional[str] = None) -> DeviceAddressMapping:
    """
    获取设备地址映射配置管理器实例（全局访问点）

    Args:
        config_dir: 配置目录路径（可选）

    Returns:
        DeviceAddressMapping: 设备地址映射配置管理器实例
    """
    return DeviceAddressMapping(config_dir=config_dir)


def load_mapping_from_file(file_path: str, return_type: str = "dict") -> Optional[Union[Dict[str, Any], JsonObject]]:
    """
    通过传入JSON文件路径获取字典或对象的对外接口

    Args:
        file_path: JSON配置文件路径:相对路径或绝对路径:相对路径相对于driver目录
        return_type: 返回类型，"dict" 返回字典，"object" 返回 JsonObject 对象

    Returns:
        Dict[str, Any] 或 JsonObject: 地址映射配置，如果未找到或加载失败则返回None
    """
    try:
        # 处理相对路径和绝对路径
        if os.path.isabs(file_path):
            full_file_path = file_path
        else:
            # # 获取当前文件所在目录作为基准目录
            # current_dir = os.path.dirname(os.path.abspath(__file__))
            # # 回退两级目录到达 driver 目录 (driver/config -> driver)
            # driver_root = os.path.dirname(current_dir)
            driver_root = ""
            # 构建相对于 driver 目录的完整路径
            full_file_path = os.path.join(driver_root, file_path)

        if os.path.exists(full_file_path):
            with open(full_file_path, 'r', encoding='utf-8') as f:
                mapping_data = json.load(f)

            if return_type == "object":
                return JsonObject(mapping_data)
            else:
                return mapping_data
        else:
            logger.warning(f"Address mapping file {full_file_path} not found")
            return None
    except json.JSONDecodeError as e:
        logger.error(f"Address mapping file {full_file_path} format error: {e}")
        return None
    except Exception as e:
        logger.error(f"Error loading address mapping from {full_file_path}: {e}")
        return None
